package com.piece.core.pay.client.impl.alipay;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.pay.client.AbstractPayClient;
import com.piece.core.pay.client.AbstractPayResponseMapping;
import com.piece.core.pay.dto.*;
import com.piece.core.pay.enums.PayNotifyRefundStatusType;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 实现支付宝支付统一接口。
 * <p>参考: https://opendocs.alipay.com/open/203/105286</p>
 */
@Slf4j
public abstract class AbstractAliPayClient extends AbstractPayClient<AliPayClientConfig> {

    protected DefaultAlipayClient client;

    public AbstractAliPayClient(String channelId, String channelCode,
                                AliPayClientConfig config, AbstractPayResponseMapping codeMapping) {
        super(channelId, channelCode, config, codeMapping);
    }

    @Override
    @SneakyThrows
    protected void doInit() {
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setAppId(config.getAppId());
        alipayConfig.setServerUrl(config.getServerUrl());
        alipayConfig.setSignType(config.getSignType());
        alipayConfig.setPrivateKey(config.getPrivateKey());
        alipayConfig.setAlipayPublicKey(config.getAlipayPublicKey());
        alipayConfig.setAlipayPublicCertContent(config.getAlipayPublicCertContent());
        alipayConfig.setAppCertContent(config.getAppCertContent());
        alipayConfig.setRootCertContent(config.getRootCertContent());
        this.client = new DefaultAlipayClient(alipayConfig);
    }

    /**
     * 解析支付单的通知结果
     */
    @Override
    public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO notifyData) throws Exception {
        if (!verifyNotifyData(notifyData)) {
            throw new Exception("签名错误");
        }
        Map<String, String> params = strToMap(notifyData.getBody());

        return  PayOrderNotifyRespDTO.builder()
                .orderExtensionNo(params.get("out_trade_no"))
                .channelOrderNo(params.get("trade_no"))
                .channelUserId(params.get("seller_id"))
                .tradeStatus(params.get("trade_status"))
                .successTime(DateUtil.getDate(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss"))
                .data(notifyData.getBody())
                .build();
    }

    /**
     * 退款实现接口
     */
    @Override
    protected AjaxResponse doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO)  {
        AlipayTradeRefundModel model=new AlipayTradeRefundModel();
        model.setTradeNo(reqDTO.getChannelOrderNo());
        model.setOutTradeNo(reqDTO.getPayTradeNo());
        model.setOutRequestNo(reqDTO.getMerchantRefundId());
        // 单位元
        model.setRefundAmount(calculateAmount(reqDTO.getAmount()).toString());
        model.setRefundReason(reqDTO.getReason());
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setBizModel(model);
        request.setNotifyUrl(StringUtil.isNotEmpty(config.getNotifyUrl()) ? config.getNotifyUrl() : reqDTO.getNotifyUrl());
        try {
            AlipayTradeRefundResponse response =  client.execute(request);
            if (response.isSuccess()) {
                // 退款导致触发的异步通知是发送到支付接口中设置的notify_url
                PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();
                respDTO.setChannelRefundId(reqDTO.getMerchantRefundId());
                return AjaxResponse.result(codeMapping.apply(response.getCode(), response.getMsg()), respDTO);
            }

            return AjaxResponse.error(response.getMsg());
        } catch (AlipayApiException e) {
            log.error("[支付宝发起退款失败,网络读超时] 请求数据: {}", JsonUtil.toJson(reqDTO), e);
            return AjaxResponse.error(e.getErrMsg());
        }
    }

    /**
     * 解析支付退款通知数据
     */
    @Override
    public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) throws Exception {
        if (!verifyNotifyData(notifyData)) {
            throw new Exception("签名错误");
        }

        if (!isRefundNotify(notifyData)) {
            throw new Exception("非退款通知");
        }

        Map<String, String> params = strToMap(notifyData.getBody());
        return PayRefundNotifyDTO.builder()
                .channelOrderNo(params.get("trade_no"))
                .tradeNo(params.get("out_trade_no"))
                .reqNo(params.get("out_biz_no"))
                .status(PayNotifyRefundStatusType.SUCCESS)
                .refundSuccessTime(DateUtil.getDate(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss"))
                .build();
    }

    /**
     * 验证是否渠道通知
     */
    @Override
    public boolean verifyNotifyData(PayNotifyDataDTO notifyData) {
        boolean verifyResult = false;
        try {
            verifyResult =  AlipaySignature.rsaCheckV1(notifyData.getParams(), config.getAlipayPublicKey(), StandardCharsets.UTF_8.name(), "RSA2");
        } catch (AlipayApiException e) {
            log.error("[支付宝验证渠道通知异常][(notify param is :{}) 验证失败]", JsonUtil.toJson(notifyData.getParams()), e);
        }
        return verifyResult;
    }

    /**
     * 判断是否为退款通知
     */
    @Override
    public boolean isRefundNotify(PayNotifyDataDTO notifyData) {
        if (notifyData.getParams().containsKey("refund_fee")) {
            return true;
        } else {
            return false;
        }
    }

    public static Map<String, String> strToMap(String s) {
        Map<String, String> stringStringMap = new HashMap<>();
        String s3 = s.replaceAll("%3A", ":");
        String s4 = s3.replace("+", " ");
        String[] split = s4.split("&");
        for (String s1 : split) {
            String[] split1 = s1.split("=");
            stringStringMap.put(split1[0], split1[1]);
        }
        return stringStringMap;
    }

    /**
     * 查询订单支付结果
     */
    @Override
    public PayOrderNotifyRespDTO tradeUnifiedQuery(String outTradeNo) {
        try {
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", outTradeNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeQueryResponse response = client.execute(request);
            return  PayOrderNotifyRespDTO.builder()
                    .orderExtensionNo(response.getOutTradeNo())
                    .channelOrderNo(response.getTradeNo())
                    .channelUserId(response.getAlipaySubMerchantId())
                    .tradeStatus(response.getTradeStatus())
                    .successTime(response.getSendPayDate())
                    .data(response.getBody())
                    .build();
        } catch (AlipayApiException e) {
            log.error("[支付宝查询支付结果失败]：{})", e);
            return PayOrderNotifyRespDTO.builder().data(e.getErrMsg()).tradeStatus("FAIL").build();
        }
    }
}
